# This code is hosted on http://code.google.com/p/lenthorp/
# Freely available for use in applications, but should NOT be modified
# Email all comments to lenthorpresearch@gmail.com

import PricingEngineMod
from PricingEngineMod import *
reload(PricingEngineMod)

from math import exp, log, sqrt, pow
from scipy.stats import norm
from numpy import interp
from scipy.integrate import quad as integ

# modelParams: zeroRates
# otherParams: swapTenor, swaptionMaturity, nu, lambda
class HullWhiteGSR(PricingEngine):

    def getPrice(self):
        if (self.currentStatus > 0):
            op = self.otherParams.p
            mp = self.modelParams.p
            self.speed = op['lambda']
            self.swaptionMaturity = op['swaptionMaturity']
            self.swapTenor = op['swapTenor']
            self.coterminal = self.swaptionMaturity + self.swapTenor
            self.zcbTenor = mp['zeroRates']['tenors']
            self.zcbRate = mp['zeroRates']['rates']
            self.forwardSwapRate = op['fwdSwapRate']
            self.annuity = 0.0
            self.annuityGFunc = 0.0
            self.gFuncCoterminal = 0.0
            self.gFuncSwaptionMaturity = 0.0         
  
            numerator = integ(self.Integrand, 1e-08, self.swaptionMaturity, limit=10)[0]
            denominator = self.swaptionMaturity * (self.annuity ** 2)
            val = op['nu']* sqrt(numerator/denominator)
            return val

    def Integrand(self, time):
        self.annuity = 0.0
        self.annuityGFunc = 0.0
        for i in range(int(self.coterminal)):
            ##self.annuity += self.zcbRate[i+1]
            ##self.annuityGFunc += self.zcbRate[i+1]*(1 - exp(-self.speed*(self.zcbTenor[i+1]-time)))/self.speed
            self.annuity += self.zcbRate[i]
            self.annuityGFunc += self.zcbRate[i]*(1 - exp(-self.speed*(self.zcbTenor[i]-time)))/self.speed

        self.gFuncCoterminal = (1 - exp(-self.speed*(int(self.coterminal)-time)))/self.speed
        ##self.gFuncSwaptionMaturity = (1 - exp(-self.speed*(self.zcbTenor[int(self.swaptionMaturity)]-time)))/self.speed
        self.gFuncSwaptionMaturity = (1 - exp(-self.speed*(int(self.swaptionMaturity)-time)))/self.speed
        I = self.forwardSwapRate * self.annuityGFunc - (self.gFuncSwaptionMaturity * self.zcbRate[int(self.swaptionMaturity)-1] - self.gFuncCoterminal * self.zcbRate[int(self.coterminal)-1])
        I = I ** 2 
        return I